/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat.web;

import java.lang.annotation.Annotation;
import java.util.TreeMap;

import pittypat.Module;
import pittypat.ModuleAnnotation;
import pittypat.ModulePresentation;
import pittypat.configuration.AppConfig;
import pittypat.configuration.ModuleConfig;
import pittypat.configuration.PittypatConfig;

/**
 * 模块信息，同时提供对 Module 对象的缓存。使用缓存可以减少对 Api 类型的反射，以及重复创建 Api 对象实例。
 */
final class ModuleInfo {
	/**
	 * 模块的路径，格式为：{开发者代码}.{应用标识符}.{模块标识符}。
	 */
	String path;
	
	/**
	 * 模块对应的 Api 类的类型。
	 */
	Class<?> type;
	
	/**
	 * 模块的 Api 类的一个对象实例。
	 */
	Module module;
	
	/**
	 * 目标应用的配置信息。
	 */
	AppConfig appConfig;
	
	/**
	 * 目标模块的配置信息。
	 */
	ModuleConfig config;
	
	/**
	 * 模块 Api 类上定义元数据。
	 */
	ModuleAnnotation annotation;
	
	/**
	 * 模块是否正在维护。
	 */
	boolean isMaintenance;
	
	/**
	 * 模块正在维护时的自定义消息。
	 */
	String maintenanceMessage;
	
	/**
	 * 模块正在维护时使用的功能信息。
	 */
	FunctionInfo maintenanceFunctionInfo;

	/**
	 * 使用模块的路径、模块类型、模块的对象实例、App 配置信息、模块的配置信息以及模块的属性初始化 ModuleInfo 类的新实例。
	 * 
	 * @param path
	 *            模块的路径。
	 * @param type
	 *            模块类型。
	 * @param module
	 *            模块的对象实例。
	 * @param appConfig
	 *            App 的配置信息。
	 * @param config
	 *            模块的配置信息。
	 * @param annotation
	 *            模块的属性。
	 */
	private ModuleInfo(String path, Class<?> type, Module module, AppConfig appConfig, ModuleConfig config,
			ModuleAnnotation annotation) {
		this.path = path;
		this.type = type;
		this.module = module;
		this.appConfig = appConfig;
		this.config = config;
		this.annotation = annotation;
		this.isMaintenance = false;
		this.maintenanceMessage = "";
		this.maintenanceFunctionInfo = null;

		// 分析应用的配置状态
		String status = appConfig.getStatus();
		status = status == null ? "" : status.toLowerCase();
		if ("维护".equalsIgnoreCase(status) || "maintenance".equalsIgnoreCase(status)) {
			// 正在维护，并且没有提供自定义的信息
			this.isMaintenance = true;
			this.maintenanceMessage = "";
		} else if (status.startsWith("维护:")) {
			// 正在维护，提供了自定义的信息
			this.isMaintenance = true;
			this.maintenanceMessage = status.substring("维护:".length());
		} else if (status.startsWith("maintenance:")) {
			// 正在维护，提供了自定义的信息
			this.isMaintenance = true;
			this.maintenanceMessage = status.substring("maintenance:".length());
		} else {
			// 应用配置中没有提供 status，分析模块配置中的 status
			status = config.getStatus();
			status = status == null ? "" : status.toLowerCase();
			if ("维护".equalsIgnoreCase(status) || "maintenance".equalsIgnoreCase(status)) {
				// 正在维护，并且没有提供自定义的信息
				this.isMaintenance = true;
				this.maintenanceMessage = "";
			} else if (status.startsWith("维护:")) {
				// 正在维护，提供了自定义的信息
				this.isMaintenance = true;
				this.maintenanceMessage = status.substring("维护:".length());
			} else if (status.startsWith("maintenance:")) {
				// 正在维护，提供了自定义的信息
				this.isMaintenance = true;
				this.maintenanceMessage = status.substring("maintenance:".length());
			}
		}

		if (this.isMaintenance) {
			// 正在维护时，默认使用的功能信息，用于向客户端发送维护信息
			this.maintenanceFunctionInfo = new FunctionInfo(null, this, null, null, null, null);
		}
	}

	/**
	 * 表示一个默认的 ModuleAnnotation，当没有在 Module 类型上声明 ModuleAnnotation 时使用此默认的
	 * ModuleAnnotation。
	 */
	private static final ModuleAnnotation DEFAULT_MODULE_ANNOTATION = new ModuleAnnotation() {
		/**
		 * 默认不允许匿名用户访问。
		 */
		@Override
		public boolean allowAnonymous() {
			return false;
		}

		/**
		 * 默认公开为 UI。
		 */
		@Override
		public ModulePresentation presentation() {
			return ModulePresentation.UI;
		}

		/**
		 * 元数据的类型。
		 */
		@Override
		public Class<? extends Annotation> annotationType() {
			return ModuleAnnotation.class;
		}
	};

	/**
	 * 查找指定路径的模块信息。如果此前已经查找过，则从缓存中返回，否则尝试创建新的模块信息对象。
	 * 
	 * @param developerCode
	 *            开发者代码。
	 * @param appIdentifier
	 *            App 的路径。
	 * @param moduleIdentifier
	 *            模块的标识符。
	 * @return 如果找到了目标模块，则返回相应模块的信息，如果没有找到，则返回 null。
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static ModuleInfo find(String developerCode, String appIdentifier, String moduleIdentifier)
			throws InstantiationException, IllegalAccessException {
		// 模块的路径
		String path = String.format("%s.%s.%s", developerCode, appIdentifier, moduleIdentifier);

		synchronized (cachedModulesLock) {
			// 首先查找缓存
			ModuleInfo info = cachedModules.get(path);

			if (info == null) {
				// 没有找到，则考虑临时创建
				// 获取应用配置信息
				AppConfig appConfig = PittypatConfig.getCurrent().getAppConfig(developerCode, appIdentifier);
				if (appConfig == null) {
					// 没有找到 App 配置
					return null;
				}

				// 获取模块配置信息
				ModuleConfig config = appConfig.getModuleConfig(moduleIdentifier);
				if (config == null) {
					return null;
				}

				// 查找模块的类型，类型名称固定为 Api
				Class<?> type = null;
				try {
					type = Class.forName(String.format("com.%s.Api", path.toLowerCase()));
				} catch (ClassNotFoundException e) {
					return null;
				}

				// 没有找到目标模块的 Api 类
				if (type == null) {
					return null;
				}

				// 模块的元数据
				ModuleAnnotation annotation = (ModuleAnnotation) type.getDeclaredAnnotation(ModuleAnnotation.class);
				if (annotation == null) {
					// 创建一个默认的 annotation
					annotation = DEFAULT_MODULE_ANNOTATION;
				}

				// 模块对象实例
				Module module = null;
				if (annotation.presentation() != ModulePresentation.INTERNAL) {
					// 只有不是内部模块时才会创建模块对象
					module = (Module) type.newInstance();
				}

				// 创建一个新的模块信息对象实例
				info = new ModuleInfo(path, type, module, appConfig, config, annotation);

				// 缓存到字典中
				cachedModules.put(path, info);
			}

			return info;
		}
	}

	// 缓存字典，字典的键为模块的完整路径。
	private static final TreeMap<String, ModuleInfo> cachedModules = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

	// 同步对缓存字典的访问。
	private static final Object cachedModulesLock = new Object();
}
